#ifndef KUN_URP_WATER_PASS_INCLUDED
#define KUN_URP_WATER_PASS_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "WaterInput.hlsl"
#include "../Core.hlsl"
#include "WaterFunction.hlsl"


struct Attributes
{
    float4 positionOS   : POSITION;
    float2 texcoord     : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct Varyings
{
    float4 positionCS               : SV_POSITION;
    float2 uv                       : TEXCOORD0;
    float3 positionWS               : TEXCOORD1;
    float3 positionVS               : TEXCOORD2;
    float4 screenPos                : TEXCOORD3;
    float3 viewDirWS                : TEXCOORD4;
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};


Varyings WaterPassVertex(Attributes input)
{
    Varyings output = (Varyings)0;

    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_TRANSFER_INSTANCE_ID(input, output);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

    output.positionCS = TransformObjectToHClip(input.positionOS);
    output.positionWS = TransformObjectToWorld(input.positionOS);
    output.positionVS = TransformWorldToView(output.positionWS);
    output.uv = input.texcoord;
    output.screenPos = ComputeScreenPos(output.positionCS);

    return output;
}

half4 WaterPassFragment(Varyings input) : SV_Target
{
    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

    float depthFactor = 1 / _DepthFactor;

    float2 delta = _Time.x * _DistortionSpeed * _DistortionDirection;

    float3 positionWS = input.positionWS;
    float3 positionVS = input.positionVS;

    float2 screenPos = input.screenPos.xy / input.screenPos.w;
    float3 sceneWorldPos = ReconstructionWorldPosFromScreenPos(screenPos);
    float depthY = positionWS.y - sceneWorldPos.y;
    float depthDistance = LinearEyeDepth(SampleCameraDepth(screenPos), _ZBufferParams) + positionVS.z;

    float4 normalMap = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, delta + TRANSFORM_TEX(sceneWorldPos.xz, _NormalMap)) * 2 - 1;
    normalMap *= _NormalScale;
    float4 normalMap2 = SAMPLE_TEXTURE2D(_NormalMap2, sampler_NormalMap2, delta * 0.75 + TRANSFORM_TEX(sceneWorldPos.xz, _NormalMap)) * 2 - 1;
    normalMap += normalMap2 * _NormalScale2;
    float3 normal = normalize(float3(normalMap.x, 1, normalMap.y));

    float3 sceneNormal = normalize(cross(ddy(sceneWorldPos), ddx(sceneWorldPos)));

    Light mainLight = GetMainLight(TransformWorldToShadowCoord(positionWS));

    float2 distortionUV = normal.xz * _DistortionScale;
    distortionUV /= 1 + abs(positionVS.z);
    distortionUV *= saturate(depthDistance);
    screenPos += distortionUV;
    sceneWorldPos = ReconstructionWorldPosFromScreenPos(screenPos);
    // return float4(sceneWorldPos, 1);
    float3 screenColor = SampleCameraColor(screenPos);

    float3 foam = Foam(depthDistance, positionWS, delta);
    
    float3 caustics = Caustics(sceneWorldPos, sceneNormal, delta);

    float fresnel = Fresnel(normal, input.viewDirWS);

    float3 reflection = SampleReflections(lerp(float3(0, 1, 0), normal, _ReflectionNormalBlend), input.viewDirWS, fresnel);
    // reflection = reflection 

    float3 spec = Highlights(_Roughness, normal, input.viewDirWS) * _SpecularColor.rgb;

    float4 finalColor = float4(0, 0, 0, 1);
    finalColor.rgb += Scattering(depthDistance * depthFactor);
    finalColor.rgb += (screenColor + caustics) * Absorption(depthDistance * depthFactor);
    finalColor.rgb += foam;
    finalColor.rgb += spec / 100;

    return finalColor;
}


///////////////////////////////////////////////////////////////////////////////
//                             Tessellation                                  //
///////////////////////////////////////////////////////////////////////////////

struct TessellationControlPoint
{
	float4  positionOS          : INTERNALTESSPOS;
	float4	texcoord 			: TEXCOORD0;	// Geometric UVs stored in xy, and world(pre-waves) in zw
	float3	positionWS			: TEXCOORD1;	// world position of the vertices
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct HS_ConstantOutput
{
	float TessFactor[3]    : SV_TessFactor;
	float InsideTessFactor : SV_InsideTessFactor;
};


float TessellationEdgeFactor(float3 p0, float3 p1) 
{
    float edgeLength = distance(p0, p1);

    float3 edgeCenter = (p0 + p1) * 0.5;
    float viewDistance = distance(edgeCenter, _WorldSpaceCameraPos);

    return edgeLength * _ScreenParams.y / (_TessellationEdgeLength * viewDistance);
}

TessellationControlPoint WaterTessellationVertex(Attributes input)
{
    TessellationControlPoint output;
    output.positionOS = input.positionOS;
    output.positionWS = TransformObjectToWorld(input.positionOS.xyz);
    output.texcoord.xy = input.texcoord;
    output.texcoord.zw = output.positionWS.xz;
    return output;
}

HS_ConstantOutput HSConstant(InputPatch<TessellationControlPoint, 3> Input )
{
    float3 p0 = TransformObjectToWorld(Input[0].positionOS.xyz);
    float3 p1 = TransformObjectToWorld(Input[1].positionOS.xyz);
    float3 p2 = TransformObjectToWorld(Input[2].positionOS.xyz);
    HS_ConstantOutput output = (HS_ConstantOutput)0;
    output.TessFactor[0] = TessellationEdgeFactor(p1, p2);
    output.TessFactor[1] = TessellationEdgeFactor(p2, p0);
    output.TessFactor[2] = TessellationEdgeFactor(p0, p1);
    output.InsideTessFactor = (TessellationEdgeFactor(p1, p2) + 
                                TessellationEdgeFactor(p2, p0) + 
                                TessellationEdgeFactor(p0, p1)) * (1 / 3.0);
    return output;
}

[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[patchconstantfunc("HSConstant")]
[outputcontrolpoints(3)]
TessellationControlPoint WaterPassHull(InputPatch<TessellationControlPoint, 3> Input, uint uCPID : SV_OutputControlPointID )
{
    return Input[uCPID];
}



// Domain: replaces vert for tessellation version
[domain("tri")]
Varyings WaterPassDomain(HS_ConstantOutput HSConstantData, const OutputPatch<TessellationControlPoint, 3> Input, float3 BarycentricCoords : SV_DomainLocation)
{
    Varyings output = (Varyings)0;
    UNITY_SETUP_INSTANCE_ID(v);
    UNITY_TRANSFER_INSTANCE_ID(v, o);
	UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
	/////////////////////Tessellation////////////////////////
	float fU = BarycentricCoords.x;
	float fV = BarycentricCoords.y;
	float fW = BarycentricCoords.z;

	float4 coords = Input[0].texcoord * fU + Input[1].texcoord * fV + Input[2].texcoord * fW;
	output.uv = coords.xy;
	output.positionWS = Input[0].positionWS * fU + Input[1].positionWS * fV + Input[2].positionWS * fW;
    output.positionWS.y += (SAMPLE_TEXTURE2D_LOD(_NormalMap, sampler_NormalMap, output.positionWS.xz * half2(0.0025, 0.0025) + _Time.x * _WaveSpeed, 0) * 2 - 1) * 0.5;
    output.positionVS = mul(UNITY_MATRIX_V, float4(output.positionWS, 1)).xyz;
    output.positionCS = mul(UNITY_MATRIX_P, float4(output.positionVS, 1));
    output.screenPos = ComputeScreenPos(output.positionCS);
    output.viewDirWS = SafeNormalize(_WorldSpaceCameraPos - output.positionWS);

    return output;
}


#endif